feat(partners): partner role row-level security (RLS) with scoped edits#21386
Open
rashad wants to merge 15 commits into
Open
feat(partners): partner role row-level security (RLS) with scoped edits#21386rashad wants to merge 15 commits into
rashad wants to merge 15 commits into
Conversation
…urface partnerUser
…ted by query engine)
…eople (when not used by another deal)
…age/amount; company & person read-only
The Partner role locked every Opportunity field except stage/amount with canUpdateFieldValue:false. But the *.updateOne pre-query hook injects updatedBy into every update, so the permission check rejected ALL partner opportunity updates (stage and amount alike) with PERMISSION_DENIED. updatedBy is overwritten with the real actor on every write, so locking it protected nothing. - Remove updatedBy from the Opportunity field-lock list (auto-written on every update). - Remove position (co-written with stage when changing stage via kanban drag). - Keep createdBy locked (not auto-written on update, still blocks tampering) and searchVector (STORED generated column, inert). - Update OPPORTUNITY_FIELD_LOCK_SKIP in configure-partner-rls.ts to match.
Contributor
Cascade (on-opportunity-partner-assigned): - Paginate the People stamp/clear fully instead of a 200-row cap, so a large company's contacts can't be left with a stale partnerUser RLS stamp (notably an ex-partner keeping visibility after unassignment). - Guard assign against clobbering a Company already owned by a different partner member (the single partnerUser column models one owner per company); stamp only the opportunity and return companyShared in that case. - Attempt every per-person update via Promise.allSettled (not fail-fast) and throw on any failure so the idempotent cascade retries; source the unassign revoke target from the event before-image so a retry still works after the opportunity's own partnerUser is cleared first. - Decide "company still in use" on partnerId (source of truth) via the before-image rather than the derived partnerUser stamp; fall back to the stamp if absent. Type-safety: - Drop all `as any` in the cascade function and its integration test; type the handler via DatabaseEventPayload<ObjectRecordUpdateEvent<CoreSchema.Opportunity>> and let the generated client / CoreSchema types flow through. Maintainability: - Export *_FIELD_ID constants from the custom Opportunity field files and use them in partner.role.ts instead of hardcoded UUIDs. - Share a single PARTNER_ROLE_LABEL constant between partner.role.ts and configure-partner-rls.ts so a role-label rename can't desync the script. Also trims verbose comments across the touched files.
e55bc43 to
1fde014
Compare
# Conflicts: # packages/twenty-apps/internal/twenty-partners/package.json # packages/twenty-apps/internal/twenty-partners/src/views/all-partners.view.ts # packages/twenty-apps/internal/twenty-partners/src/views/validated-partners.view.ts
🔍 Automated Pre-Review✅ No issues detected - This PR is ready for human review. Summary
Automated pre-review — human approval still required. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an external Partner self-service role that sees and edits only its own
records via row-level security (RLS), so a validated partner can sign in and manage
just the deals they're matched on.
What's included
partnerUserrelation on Partner, Person, Company, Opportunity (+ inverserelations on Workspace Member) — the login member a record belongs to.
workspace member", plus a self-scope on Workspace Member so member-typed relations
resolve without exposing the internal team roster. Applied out-of-band via
yarn rls:configure(the app manifest cannot ship RLS predicates).on-opportunity-partner-assignedlogic function):assigning a Partner to an Opportunity stamps
partnerUseronto the Opportunity +its Company + People; removing the Partner clears it (and cascades to the
Company/People when no other deal of that member still uses them).
stageandamountonly (every otheruser-facing field locked).
partnerUsercolumn added to the Validated Partners view so the login membercan be assigned inline.
Install / upgrade note
After install or reinstall, run
yarn rls:configure(:prodvariant for prod) to(re)apply the RLS predicates and verify the field-permission locks. Manifest sync
handles object/field permissions; predicates are applied by this script.
Platform gaps found (for the eng team)
changes deployed via
yarn twenty dev --oncepersist to the DB but aren't reflectedin the cached snapshot used for enforcement until
engine:workspace:metadata:permissions:roles-permissions:<workspaceId>:{data,hash}is flushed. Relevant on any real workspace when permissions change.
*.updateOnepre-query hook writes
updatedByinto every update, socanUpdateFieldValue:falseon
updatedBymakes the permission check reject every record update withPERMISSION_DENIED. Field-permission lock lists must exclude server-managed/injectedfields (
updatedBy; andposition, co-written withstageon kanban drag).Version
Minor bump →
0.5.0(new role, new fields, new behaviour; backwards-compatible).Testing
amount; Company/Person read-only; sees only matched deals; unassigning a partner
removes the deal (and its company/people) from the partner's view.
yarn rls:configurepasses (5 predicates upserted; 24 Opportunity fields locked,stage + amount editable).